//
// CDXGraph.cpp
//

#include "stdafx.h"
#include "PubDefs.h"
#include "CDXGraph.h"
#include "safe_defs.h"
#include "DxFuncs.h"
#include <dvdmedia.h>

//#ifdef _DEBUG
//#define new DEBUG_NEW
//#undef THIS_FILE
//static char THIS_FILE[] = __FILE__;
//#endif

#pragma comment(lib, "Comdlg32.lib")
#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "strmbasd.lib")

// {E3FB4BFE-8E5C-4aec-8162-7DA55BE486A1}
DEFINE_GUID(CLSID_HQTitleOverlay, 
			0xe3fb4bfe, 0x8e5c, 0x4aec, 0x81, 0x62, 0x7d, 0xa5, 0x5b, 0xe4, 0x86, 0xa1);

////////////////////////////////////////////////////////////////////////////////
CDXGraph::CDXGraph()
{
	mGraph        = NULL;
	mMediaControl = NULL;
	mEvent        = NULL;
	mBasicVideo   = NULL;
	mBasicAudio   = NULL;
	mVideoWindow  = NULL;
	mSeeking      = NULL;
	mTitleOverlay = NULL;
	mTitleOverlaySetting = NULL;
	m_pVideoRenderOld = NULL;
	mObjectTableEntry = 0;
}

CDXGraph::~CDXGraph()
{
	Release();
}
//
//REGDB_E_CLASSNOTREG 
//A specified class is not registered in the registration database. Also can indicate that the type of server you requested in the CLSCTX enumeration is not registered or the values for the server types in the registry are corrupt.
//
//CLASS_E_NOAGGREGATION 
//This class cannot be created as part of an aggregate.
//
//E_NOINTERFACE

bool CDXGraph::Create(void)
{
	Release();
	if (!mGraph)
	{
		if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
			IID_IGraphBuilder, (void **)&mGraph)))
		{
			AddToObjectTable();
			return QueryInterfaces();
		}
		mGraph = NULL;
	}
	return false;
}

bool CDXGraph::QueryInterfaces(void)
{
	if (mGraph)
	{
		HRESULT hr = NOERROR;
		hr |= mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
		hr |= mGraph->QueryInterface(IID_IMediaEventEx, (void **)&mEvent);
		hr |= mGraph->QueryInterface(IID_IBasicVideo, (void **)&mBasicVideo);
		hr |= mGraph->QueryInterface(IID_IBasicAudio, (void **)&mBasicAudio);
		hr |= mGraph->QueryInterface(IID_IVideoWindow, (void **)&mVideoWindow);
		hr |= mGraph->QueryInterface(IID_IMediaSeeking, (void **)&mSeeking);
		if (mSeeking)
		{
			mSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
		}
		return SUCCEEDED(hr);
	}
	return false;
}

void CDXGraph::Release(void)
{
	SAFE_RELEASE(m_pVideoRenderOld);
	SAFE_RELEASE(mTitleOverlaySetting);
	SAFE_RELEASE(mTitleOverlay);
	if (mSeeking)
	{
		mSeeking->Release();
		mSeeking = NULL;
	}
	if (mMediaControl)
	{
		mMediaControl->Release();
		mMediaControl = NULL;
	}
	if (mEvent)
	{
		mEvent->Release();
		mEvent = NULL;
	}
	if (mBasicVideo)
	{
		mBasicVideo->Release();
		mBasicVideo = NULL;
	}
	if (mBasicAudio)
	{
		mBasicAudio->Release();
		mBasicAudio = NULL;
	}
	if (mVideoWindow)
	{
		mVideoWindow->put_Visible(OAFALSE);
		mVideoWindow->put_MessageDrain((OAHWND)NULL);
		mVideoWindow->put_Owner(OAHWND(0));
		mVideoWindow->Release();
		mVideoWindow = NULL;
	}
	RemoveFromObjectTable();
	if (mGraph) 
	{
		mGraph->Release(); 
		mGraph = NULL;
	}
}

bool CDXGraph::Attach(IGraphBuilder * inGraphBuilder)
{
	Release();

	if (inGraphBuilder)
	{
		inGraphBuilder->AddRef();
		mGraph = inGraphBuilder;

		AddToObjectTable();
		return QueryInterfaces();
	}
	return true;
}

IGraphBuilder * CDXGraph::GetGraph(void)
{
	return mGraph;
}

IMediaEventEx * CDXGraph::GetEventHandle(void)
{
	return mEvent;
}

// Connect filter from the upstream output pin to the downstream input pin
bool CDXGraph::ConnectFilters(IPin * inOutputPin, IPin * inInputPin, 
							  const AM_MEDIA_TYPE * inMediaType)
{
	if (mGraph && inOutputPin && inInputPin)
	{
		HRESULT hr = mGraph->ConnectDirect(inOutputPin, inInputPin, inMediaType);
		return SUCCEEDED(hr) ? true : false;
	}
	return false;
}

void CDXGraph::DisconnectFilters(IPin * inOutputPin)
{
	if (mGraph && inOutputPin)
	{
		HRESULT hr = mGraph->Disconnect(inOutputPin);
	}
}

bool CDXGraph::SetDisplayWindow(HWND inWindow)
{	
	if (mVideoWindow)
	{
		// Hide the video window first
		mVideoWindow->put_Visible(OAFALSE);
		mVideoWindow->put_Owner((OAHWND)inWindow);

		if (inWindow != NULL)
		{
			RECT windowRect;
			::GetClientRect(inWindow, &windowRect);
			mVideoWindow->put_Left(0);
			mVideoWindow->put_Top(0);
			mVideoWindow->put_Width(windowRect.right - windowRect.left);
			mVideoWindow->put_Height(windowRect.bottom - windowRect.top);
			mVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);

			mVideoWindow->put_MessageDrain((OAHWND) inWindow);
		}
		// Restore the video window
		if (inWindow != NULL)
		{
			mVideoWindow->put_Visible(OATRUE);
		}
		else
		{
			mVideoWindow->put_Visible(OAFALSE);
		}
		return true;
	}
	return false;
}

bool CDXGraph::ResizeVideoWindow(long inLeft, long inTop, long inWidth, long inHeight)
{
	if (mVideoWindow)
	{
		long lVisible = OATRUE;
		mVideoWindow->get_Visible(&lVisible);
		// Hide the video window first
		mVideoWindow->put_Visible(OAFALSE);

		mVideoWindow->put_Left(inLeft);
		mVideoWindow->put_Top(inTop);
		mVideoWindow->put_Width(inWidth);
		mVideoWindow->put_Height(inHeight);
	
		// Restore the video window
		mVideoWindow->put_Visible(lVisible);
		return true;
	}
	return false;
}

bool CDXGraph::SetNotifyWindow(HWND inWindow)
{
	if (mEvent)
	{
		mEvent->SetNotifyWindow((OAHWND)inWindow, WM_GRAPHNOTIFY, 0);
		return true;
	}
	return false;
}

void CDXGraph::HandleEvent(WPARAM inWParam, LPARAM inLParam)
{
	if (mEvent)
	{
		LONG eventCode = 0, eventParam1 = 0, eventParam2 = 0;
		while (SUCCEEDED(mEvent->GetEvent(&eventCode, &eventParam1, &eventParam2, 0)))
		{
			mEvent->FreeEventParams(eventCode, eventParam1, eventParam2);
			switch (eventCode)
			{
			case EC_COMPLETE:
				break;

			case EC_USERABORT:
			case EC_ERRORABORT:
				break;

			default:
				break;
			}
		}
	}
}

bool CDXGraph::Run(void)
{
	if (mGraph && mMediaControl)
	{
		if (!IsRunning())
		{
			//SetCurrentPosition(5);
			HRESULT hr = mMediaControl->Run();
			if (SUCCEEDED(hr))
			{
				//test
				//IBaseFilter* pVideoRender = NULL;
				//HRESULT hr = FindFilterByInterface(IID_IVideoWindow, &pVideoRender);
				
				//IPin* pPinIn = InputPinOf(pVideoRender);
				//if (!pPinIn) 
				//{
				//	return false;
				//}

				//AM_MEDIA_TYPE mediaType;
				//hr = pPinIn->ConnectionMediaType(&mediaType);
				//VIDEOINFOHEADER* pHeader = NULL;
				//VIDEOINFOHEADER2* pHeader2 = NULL;
				//if(mediaType.formattype == FORMAT_VideoInfo)
				//{
				//	pHeader = (VIDEOINFOHEADER*)mediaType.pbFormat;
				//}
				//else if(mediaType.formattype == FORMAT_VideoInfo2)
				//{
				//	pHeader2 = (VIDEOINFOHEADER2*)mediaType.pbFormat;
				//}

				//  end test
				return true;
			}
			else
			{
				g_Log.Error(_T("mMediaControl->Run Error HResult:0x%X"), hr);
			}
		}
		else
		{
			return true;
		}
	}
	return false;
}

bool CDXGraph::Stop(void)
{
	if (mGraph && mMediaControl)
	{
		if (!IsStopped())
		{	
			if (SUCCEEDED(mMediaControl->Stop()))
			{
				return true;
			}
		}
		else
		{
			return true;
		}
	}
	return false;
}

bool CDXGraph::Pause(void)
{
	if (mGraph && mMediaControl)
	{
		if (!IsPaused())
		{	
			if (SUCCEEDED(mMediaControl->Pause()))
			{
				return true;
			}
		}
		else
		{
			return true;
		}
	}
	return false;
}

bool CDXGraph::IsRunning(void)
{
	if (mGraph && mMediaControl)
	{
		OAFilterState state = State_Stopped;
		if (SUCCEEDED(mMediaControl->GetState(10, &state)))
		{
			return state == State_Running;
		}
	}
	return false;
}

bool CDXGraph::IsStopped(void)
{
	if (mGraph && mMediaControl)
	{
		OAFilterState state = State_Stopped;
		if (SUCCEEDED(mMediaControl->GetState(10, &state)))
		{
			return state == State_Stopped;
		}
	}
	return false;
}

bool CDXGraph::IsPaused(void)
{
	if (mGraph && mMediaControl)
	{
		OAFilterState state = State_Stopped;
		if (SUCCEEDED(mMediaControl->GetState(10, &state)))
		{
			return state == State_Paused;
		}
	}
	return false;
}

bool CDXGraph::SetFullScreen(BOOL inEnabled)
{
	if (mVideoWindow)
	{
		HRESULT hr = mVideoWindow->put_FullScreenMode(inEnabled ? OATRUE : OAFALSE);
		return SUCCEEDED(hr);
	}
	return false;
}

bool CDXGraph::GetFullScreen(void)
{
	if (mVideoWindow)
	{
		long  fullScreenMode = OAFALSE;
		mVideoWindow->get_FullScreenMode(&fullScreenMode);
		return (fullScreenMode == OATRUE);
	}
	return false;
}

// IMediaSeeking features
bool CDXGraph::GetCurrentPosition(double * outPosition)
{
	if (mSeeking)
	{
		__int64 position = 0;
		if (SUCCEEDED(mSeeking->GetCurrentPosition(&position)))
		{
			*outPosition = ((double)position) / 10000000.;
			return true;
		}
	}
	return false;
}

bool CDXGraph::GetStopPosition(double * outPosition)
{
	if (mSeeking)
	{
		__int64 position = 0;
		if (SUCCEEDED(mSeeking->GetStopPosition(&position)))
		{
			*outPosition = ((double)position) / 10000000.;
			return true;
		}
	}
	return false;
}

bool CDXGraph::SetCurrentPosition(double inPosition)
{
	if (mSeeking)
	{
		__int64 one = 10000000;
		__int64 position = (__int64)(one * inPosition);
		HRESULT hr = mSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 
			0, AM_SEEKING_NoPositioning);
		return SUCCEEDED(hr);
	}
	return false;
}

bool CDXGraph::SetStartStopPosition(double inStart, double inStop)
{
	if (mSeeking)
	{
		__int64 one = 10000000;
		__int64 startPos = (__int64)(one * inStart);
		__int64 stopPos  = (__int64)(one * inStop);
		HRESULT hr = mSeeking->SetPositions(&startPos, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 
			&stopPos, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame);
		return SUCCEEDED(hr);
	}
	return false;
}

bool CDXGraph::GetDuration(double * outDuration)
{
	if (mSeeking)
	{
		__int64 length = 0;
		if (SUCCEEDED(mSeeking->GetDuration(&length)))
		{
			*outDuration = ((double)length) / 10000000.;
			return true;
		}
	}
	return false;
}

bool CDXGraph::SetPlaybackRate(double inRate)
{
	if (mSeeking)
	{
		if (SUCCEEDED(mSeeking->SetRate(inRate)))
		{
			return true;
		}
	}
	return false;
}

// Attention: range from -10000 to 0, and 0 is FULL_VOLUME.
bool CDXGraph::SetAudioVolume(long inVolume)
{
	if (mBasicAudio)
	{
		HRESULT hr = mBasicAudio->put_Volume(inVolume);
		return SUCCEEDED(hr);
	}
	return false;
}

long CDXGraph::GetAudioVolume(void)
{
	long volume = 0;
	if (mBasicAudio)
	{
		mBasicAudio->get_Volume(&volume);
	}
	return volume;
}

// Attention: range from -10000(left) to 10000(right), and 0 is both.
bool CDXGraph::SetAudioBalance(long inBalance)
{
	if (mBasicAudio)
	{
		HRESULT hr = mBasicAudio->put_Balance(inBalance);
		return SUCCEEDED(hr);
	}
	return false;
}

long CDXGraph::GetAudioBalance(void)
{
	long balance = 0;
	if (mBasicAudio)
	{
		mBasicAudio->get_Balance(&balance);
	}
	return balance;
}

HRESULT CDXGraph::RenderFile(CString inFile)
{
	if(mGraph == NULL)
		return E_POINTER;

	WCHAR wFile[MAX_PATH];
	CStringToWChar(wFile, MAX_PATH, inFile);
	return mGraph->RenderFile(wFile, NULL);
}

bool CDXGraph::SnapshotBitmap(CString outFile)
{
	if (mBasicVideo)
	{
		long bitmapSize = 0;
		if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))
		{
			bool pass = false;
			unsigned char * buffer = new unsigned char[bitmapSize];
			if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
			{
				BITMAPFILEHEADER	hdr;
				LPBITMAPINFOHEADER	lpbi;

				lpbi = (LPBITMAPINFOHEADER)buffer;

				int nColors = 1 << lpbi->biBitCount;
				if (nColors > 256)
					nColors = 0;

				hdr.bfType		= ((WORD) ('M' << 8) | 'B');	//always is "BM"
				hdr.bfSize		= bitmapSize + sizeof( hdr );
				hdr.bfReserved1 	= 0;
				hdr.bfReserved2 	= 0;
				hdr.bfOffBits		= (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize +
						nColors * sizeof(RGBQUAD));

				CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
				bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));
				bitmapFile.Write(buffer, bitmapSize);
				bitmapFile.Close();
				pass = true;
			}
			delete [] buffer;
			return pass;
		}
	}
	return false;
}



//////////////////////// For GraphEdit Dubug purpose /////////////////////////////
void CDXGraph::AddToObjectTable(void)
{
	IMoniker * pMoniker = 0;
    IRunningObjectTable * objectTable = 0;
    if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) 
	{
		WCHAR wsz[256];
		wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)mGraph, GetCurrentProcessId());
		HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
		if (SUCCEEDED(hr)) 
		{
			hr = objectTable->Register(0, mGraph, pMoniker, &mObjectTableEntry);
			pMoniker->Release();
		}
		objectTable->Release();
	}
}

void CDXGraph::RemoveFromObjectTable(void)
{
	IRunningObjectTable * objectTable = 0;
    if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) 
	{
        objectTable->Revoke(mObjectTableEntry);
        objectTable->Release();
		mObjectTableEntry = 0;
    }
}



//////////////////////////////////////////////////////////////////////////
// For Title Overlay Filter

//HRESULT CDXGraph::WriteFileWithTitleOverlay(CString inFile, CString outFile)
//{
//	if (mGraph == NULL)
//		return E_POINTER;
//
//	IBaseFilter* pVideoRender = NULL;
//	IBaseFilter* pAudioRender = NULL;
//	HRESULT hr = S_OK;
//	do 
//	{
//		hr = RenderFile(inFile);
//		BREAK_IF_FAILED(hr);
//
//		hr = FindFilterByInterface(IID_IVideoWindow, &pVideoRender);
//		BREAK_IF_FAILED(hr, _T("FindFilterByInterface"));
//
//		hr = FindFilterByInterface(IID_IBasicAudio, &pAudioRender);
//		BREAK_IF_FAILED(hr);
//
//		IPin* pAudioPin = InputPinOf(pAudioRender);
//		AM_MEDIA_TYPE mtAudio;
//		pAudioPin->ConnectionMediaType(&mtAudio);
//		pAudioPin->Release();
//		CMediaType mtInAudio = mtAudio;
//		FreeMediaType(mtAudio);
//
//		IPin* pPin = InputPinOf(pVideoRender);
//		AM_MEDIA_TYPE mt;
//		pPin->ConnectionMediaType(&mt);
//		pPin->Release();
//		CMediaType mtIn = mt;
//		FreeMediaType(mt);
//
//		if (mtIn.subtype == MEDIASUBTYPE_Overlay)
//		{
//			IBaseFilter* pOvMix = NULL;
//			hr = NextUpstream(pVideoRender, &pOvMix);
//			BREAK_IF_FAILED(hr, _T("NextUpstream"));
//			pVideoRender = pOvMix;
//		}
//
//		hr = CreateTitleOverlayFilter();
//		BREAK_IF_FAILED(hr, _T("CreateTitleOverlayFilter"));
//		hr = SetTiltleOverlayStyle();
//		BREAK_IF_FAILED(hr);
//		hr = InsertTitleOverlayFilter(pVideoRender, mTitleOverlay);
//		BREAK_IF_FAILED(hr);
//
//		hr = m_fileWriter.Init(mGraph, outFile);
//		BREAK_IF_FAILED(hr);
//
//		hr = SubstituteRenderers(m_fileWriter.GetPointer(), pVideoRender, pAudioRender);
//
//	} while (false);
//
//	SAFE_RELEASE(pAudioRender);
//	SAFE_RELEASE(pVideoRender);
//	return hr;
//}


HRESULT CDXGraph::WriteFileWithTitleOverlay(CString inFile, CString outFile)
{
	IBaseFilter* pVideoRender = NULL;
	IBaseFilter* pAudioRender = NULL;
	HRESULT hr = S_OK;
	do 
	{
		hr = RenderFileWithTitleOverlay(inFile);
		BREAK_IF_FAILED(hr, _T("RenderFileWithTitleOverlay"));

		hr = FindFilterByInterface(IID_IVideoWindow, &pVideoRender);
		BREAK_IF_FAILED(hr, _T("FindFilterByInterface"));

		hr = FindFilterByInterface(IID_IBasicAudio, &pAudioRender);
		BREAK_IF_FAILED(hr, _T("FindFilterByInterface IID_IBasicAudio"));

		hr = m_fileWriter.Init(mGraph, outFile);
		BREAK_IF_FAILED(hr, _T("m_fileWriter.Init"));

		hr = SubstituteRenderers(m_fileWriter.GetPointer(), pVideoRender, pAudioRender);
		BREAK_IF_FAILED(hr, _T("SubstituteRenderers"));

	} while (false);

	SAFE_RELEASE(pAudioRender);
	SAFE_RELEASE(pVideoRender);
	return hr;
}

HRESULT CDXGraph::RenderFileWithVideoRendererOld(CString inFile)
{
	IBaseFilter* pVideoRender = NULL;
	HRESULT hr = S_OK;
	do 
	{
		hr = RenderFileWithTitleOverlay(inFile);
		BREAK_IF_FAILED(hr, _T("RenderFileWithTitleOverlay"));

		hr = FindFilterByInterface(IID_IVideoWindow, &pVideoRender);
		BREAK_IF_FAILED(hr, _T("FindFilterByInterface"));

		hr = DxFuncs::AddFilterByCLSID(mGraph, CLSID_VideoRenderer, L"Video Renderer Old", &m_pVideoRenderOld);
		BREAK_IF_FAILED(hr, _T("AddFilterByCLSID CLSID_VideoRenderer"));

		hr = SubstituteVideoRendererWithOld(pVideoRender, m_pVideoRenderOld);
		BREAK_IF_FAILED(hr, _T("SubstituteVideoRendererWithOld"));

	} while (false);

	SAFE_RELEASE(pVideoRender);
	return hr;
}


HRESULT CDXGraph::RenderFileWithTitleOverlay(CString inFile)
{
	if (mGraph == NULL)
		return E_POINTER;

	IBaseFilter* pVideoRender = NULL;
	HRESULT hr = S_OK;
	do 
	{
		hr = RenderFile(inFile);
		BREAK_IF_FAILED(hr, _T("RenderFile") + inFile);

		hr = FindFilterByInterface(IID_IVideoWindow, &pVideoRender);
		BREAK_IF_FAILED(hr, _T("FindFilterByInterface"));

		IPin* pPin = InputPinOf(pVideoRender);
		AM_MEDIA_TYPE mt;
		pPin->ConnectionMediaType(&mt);
		pPin->Release();
		CMediaType mtIn = mt;
		FreeMediaType(mt);

		if (mtIn.subtype == MEDIASUBTYPE_Overlay)
		{
			IBaseFilter* pOvMix = NULL;
			hr = NextUpstream(pVideoRender, &pOvMix);
			BREAK_IF_FAILED(hr, _T("NextUpstream"));
			pVideoRender = pOvMix;
		}

		hr = CreateTitleOverlayFilter();
		BREAK_IF_FAILED(hr, _T("CreateTitleOverlayFilter"));

		hr = SetTiltleOverlayStyle();
		BREAK_IF_FAILED(hr, _T("SetTiltleOverlayStyle"));

		hr = InsertTitleOverlayFilter(pVideoRender, mTitleOverlay);
		BREAK_IF_FAILED(hr, _T("InsertTitleOverlayFilter"));

	} while (false);

	SAFE_RELEASE(pVideoRender);
	return hr;
}

HRESULT CDXGraph::CreateTitleOverlayFilter()
{
	HRESULT hr = DxFuncs::AddFilterByCLSID(mGraph, CLSID_HQTitleOverlay, L"Title Overlay", &mTitleOverlay);
	if (SUCCEEDED(hr))
	{
		mTitleOverlay->QueryInterface(IID_ITitleOverlay, (void **)&mTitleOverlaySetting);
	}
	return hr;
}

HRESULT CDXGraph::SetTiltleOverlayStyle()
{
	if (mTitleOverlaySetting == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;
	//hr = mTitleOverlaySetting->put_TitleOverlayType(2);
	//hr = mTitleOverlaySetting->put_TitleDuration(3, 4);
	return hr;
}

HRESULT CDXGraph::SetScoreFont()
{
	if (mTitleOverlaySetting == NULL)
		return E_POINTER;

	LOGFONT font;
	mTitleOverlaySetting->get_TitleFont(&font);
	if (ShowSelectFontDlg(&font))
	{
		mTitleOverlaySetting->put_TitleFont(font);
	}
	return S_OK;
}

HRESULT CDXGraph::ChangeScore(CString strScore)
{
	if (mTitleOverlaySetting == NULL)
		return E_POINTER;

	USES_CONVERSION;
	char czTitle[MAX_PATH];
	sprintf_s(czTitle, T2A((LPTSTR)(LPCTSTR)strScore));
	czTitle[MAX_PATH-1] = 0;

	return mTitleOverlaySetting->changeTitleRealTime(czTitle, strlen(czTitle));

	return S_OK;
}

bool CDXGraph::ShowSelectFontDlg(LOGFONT* pFont)
{
	//CHOOSEFONT    cf;
	//// Initialize CHOOSEFONT
	//ZeroMemory(&cf, sizeof(CHOOSEFONT));
	//cf.lStructSize = sizeof(CHOOSEFONT);
	//cf.hInstance   = g_hInst;
	//cf.hwndOwner   = NULL;
	//cf.lpLogFont   = pFont;
	////cf.rgbColors   = mTitleColor;
	//cf.Flags       = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;
	//return ChooseFont(&cf);
	return false;
}

// Follow the pin connections to return the filter that is 
// connected to the first input pin of pFilter
HRESULT CDXGraph::NextUpstream(IBaseFilter* pFilter, IBaseFilter** ppNext)
{
	IPin* pPin = InputPinOf(pFilter);
	if (!pPin) 
	{
		return E_FAIL;
	}

	// Get the peer output pin
	IPin* pPinOut = NULL;
	HRESULT hr = pPin->ConnectedTo(&pPinOut);
	pPin->Release();
	if (FAILED(hr)) 
	{
		return hr;
	}

	PIN_INFO info;
	pPinOut->QueryPinInfo(&info);
	pPinOut->Release();
	*ppNext = info.pFilter;

	return S_OK;
}

void CDXGraph::EnumMediaTypes(IPin* pPin)
{
	IEnumMediaTypes* pEnumMediaTypes = NULL;
	pPin->EnumMediaTypes(&pEnumMediaTypes);
	CMediaType* pMediaType = NULL;
	ULONG ulMediaCount = 0;

	HRESULT hr = S_OK;
	int count = 0;
	for (;;)
	{
		hr = pEnumMediaTypes->Next(1, (AM_MEDIA_TYPE**)&pMediaType, &ulMediaCount);
		if(hr != S_OK)
			break;

		PrintMediaType(pMediaType);
		count++;
	}
	g_Log.Info(_T("Media Type Count:%d"), count);
	DeleteMediaType(pMediaType);
	pEnumMediaTypes->Release();
}

void CDXGraph::PrintMediaType(AM_MEDIA_TYPE* pMediaType)
{
	CString videoInfoType;
	int width = 0;
	int height = 0;
	DWORD interlace = 0;
	if (pMediaType->majortype == MEDIATYPE_Video &&
		pMediaType->subtype == MEDIASUBTYPE_RGB32)
	{
		if (pMediaType->formattype == FORMAT_VideoInfo)
		{
			videoInfoType = _T("VideoInfo");
			VIDEOINFOHEADER * pHeader = (VIDEOINFOHEADER *) pMediaType->pbFormat;
			width = pHeader->bmiHeader.biWidth;
			height = pHeader->bmiHeader.biHeight;
		}
		else if (pMediaType->formattype == FORMAT_VideoInfo2)
		{
			videoInfoType = _T("VideoInfo2");
			VIDEOINFOHEADER2 * pHeader = (VIDEOINFOHEADER2 *) pMediaType->pbFormat;
			width = pHeader->bmiHeader.biWidth;
			height = pHeader->bmiHeader.biHeight;
			interlace = pHeader->dwInterlaceFlags;
		}
		else
		{
			videoInfoType = _T("Other");
		}

		g_Log.Info(_T("Format:Video\tRGB32\t%s\tcbFormat:%d\tWidth:%d\tHeight:%d\tInterlace:0x%X"), 
			videoInfoType, pMediaType->cbFormat, width, height, interlace);
	}
}

HRESULT CDXGraph::DropFilter(IBaseFilter* pFilter, IPin** ppPinUpstream)
{
	if(pFilter == NULL || ppPinUpstream == NULL)
		return E_POINTER;
	*ppPinUpstream = NULL;

	IPin* pPinIn = InputPinOf(pFilter);
	if (!pPinIn) 
	{
		return E_FAIL;
	}

	HRESULT hr = S_OK;
	do 
	{
		// Get the peer output pin
		IPin* pPinOut = NULL;
		hr = pPinIn->ConnectedTo(&pPinOut);
		BREAK_IF_FAILED(hr, _T("DXGraph ConnectedTo"));

		// Disconnect the current connection
		hr = mGraph->Disconnect(pPinOut);
		BREAK_IF_FAILED(hr, _T("DXGraph Disconnect"));

		hr = mGraph->Disconnect(pPinIn);
		BREAK_IF_FAILED(hr, _T("DXGraph Disconnect"));

		hr = mGraph->RemoveFilter(pFilter);
		BREAK_IF_FAILED(hr, _T("DXGraph RemoveFilter"));
		
		*ppPinUpstream = pPinOut;
	} while (false);

	SAFE_RELEASE(pPinIn);
	return hr;
}

// Connect the filter pTransform upstream of pFilter by reconnecting pins.
// Assumes that pTransform has only one input and one output, and
// that pFilter has only one input.
HRESULT CDXGraph::InsertTitleOverlayFilter(IBaseFilter* pFilter, IBaseFilter* pTransform)
{
	IPin* pPinIn = InputPinOf(pFilter);
	if (!pPinIn) 
	{
		return E_FAIL;
	}

	// Get the peer output pin
	IPin* pPinOut = NULL;
	HRESULT hr = pPinIn->ConnectedTo(&pPinOut);
	if (FAILED(hr)) 
	{
		pPinIn->Release();
		return hr;
	}

	// Disconnect the current connection
	hr = mGraph->Disconnect(pPinOut);
	if (SUCCEEDED(hr)) 
	{
		hr = mGraph->Disconnect(pPinIn);
	}

	// Insert pTransform filter by connecting its input pin and output pin

	// EnumMediaTypes(pPinOut);

	IEnumMediaTypes* pEnumMediaTypes = NULL;
	pPinOut->EnumMediaTypes(&pEnumMediaTypes);
	CMediaType* pMediaType = NULL;
	ULONG ulMediaCount = 0;

	for (;;)
	{
		hr = pEnumMediaTypes->Next(1, (AM_MEDIA_TYPE**)&pMediaType, &ulMediaCount);
		if(hr != S_OK)
			break;
		if (pMediaType->majortype == MEDIATYPE_Video &&
			pMediaType->subtype == MEDIASUBTYPE_RGB32)
		{
			hr = TryInsertTitleOverlayFilter(pTransform, pPinOut, pPinIn, pMediaType);
			if (SUCCEEDED(hr))
			{
				g_Log.Info(_T("Connect Succeeded:"));
				PrintMediaType(pMediaType);
				break;
			}
		}
		if(!pMediaType)
		{
			DeleteMediaType(pMediaType);
			pMediaType = NULL;
		}
	}

	//AM_MEDIA_TYPE mediaType;
	//hr = pPinOut->ConnectionMediaType(&mediaType);
	//VIDEOINFOHEADER* pHeader = NULL;
	//VIDEOINFOHEADER2* pHeader2 = NULL;
	//if(mediaType.formattype == FORMAT_VideoInfo)
	//{
	//	pHeader = (VIDEOINFOHEADER*)mediaType.pbFormat;
	//}
	//else if(mediaType.formattype == FORMAT_VideoInfo2)
	//{
	//	pHeader2 = (VIDEOINFOHEADER2*)mediaType.pbFormat;
	//}

	if(!pMediaType)
	{
		DeleteMediaType(pMediaType);
		pMediaType = NULL;
	}
	pEnumMediaTypes->Release();

	pPinIn->Release();
	pPinOut->Release();
	return hr;
}

HRESULT ConnectPins(IGraphBuilder* pGraph, IPin* pPinOut, IPin* pPinIn)
{
	IEnumMediaTypes* pEnumMediaTypes = NULL;
	pPinOut->EnumMediaTypes(&pEnumMediaTypes);
	CMediaType* pMediaType = NULL;
	ULONG ulMediaCount = 0;

	HRESULT hr = S_OK;
	for (;;)
	{
		hr = pEnumMediaTypes->Next(1, (AM_MEDIA_TYPE**)&pMediaType, &ulMediaCount);
		if(hr != S_OK)
			break;
		hr = pGraph->Connect(pPinOut, pPinIn);
		//hr = pGraph->ConnectDirect(pPinOut, pPinIn, pMediaType);
		if (SUCCEEDED(hr))
		{
			break;
		}
		if(!pMediaType)
		{
			DeleteMediaType(pMediaType);
			pMediaType = NULL;
		}
	}
	if(!pMediaType)
	{
		DeleteMediaType(pMediaType);
		pMediaType = NULL;
	}
	pEnumMediaTypes->Release();
	return (S_FALSE ? E_FAIL : hr);
}

HRESULT TryConnectPins(IGraphBuilder* pGraph, IPin* pPinOut, IPin* pPinIn, AM_MEDIA_TYPE* pMediaType)
{
	if (pGraph == NULL || pPinOut == NULL || pPinIn == NULL)
		return E_POINTER;

	return pGraph->ConnectDirect(pPinOut, pPinIn, pMediaType);
}

HRESULT CDXGraph::SubstituteVideoRendererWithOld(IBaseFilter* pVideoRender, IBaseFilter* pVideoRenderOld)
{
	if (pVideoRender == NULL || pVideoRenderOld == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;
	IPin* pVideoOutPin = NULL;
	IPin* pVideoInPin = NULL;
	do 
	{

		hr = DropFilter(pVideoRender, &pVideoOutPin);
		BREAK_IF_FAILED(hr, _T("DropFilter pVideoRender"));

		hr = E_FAIL;
		pVideoInPin = InputPinOf(pVideoRenderOld);
		BREAK_IF_NULL(pVideoInPin, _T("SubstituteVideoRendererWithOld InputPinOf(pVideoRenderOld)"))

		hr = ConnectPins(mGraph, pVideoOutPin, pVideoInPin);
		if (FAILED(hr))
		{
			g_Log.Error(_T("ConnectPins(mGraph, pVideoOutPin, pVideoInPin)"));
		}
		BREAK_IF_FAILED(hr, _T("ConnectPins(mGraph, pVideoOutPin, pVideoInPin)"));

	} while (false);
	SAFE_RELEASE(pVideoInPin);
	SAFE_RELEASE(pVideoOutPin);
	return hr;
}

HRESULT CDXGraph::SubstituteRenderers(IBaseFilter* pFileWriter, IBaseFilter* pVideoRender, IBaseFilter* pAudioRender)
{
	if(pFileWriter == NULL || pVideoRender == NULL || pAudioRender == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;
	IPin* pVideoOutPin = NULL;
	IPin* pAudioOutPin = NULL;
	IPin* pVideoInPin = NULL;
	IPin* pAudioInPin = NULL;
	do 
	{
		
		hr = DropFilter(pVideoRender, &pVideoOutPin);
		BREAK_IF_FAILED(hr, _T("DropFilter pVideoRender"));

		hr = DropFilter(pAudioRender, &pAudioOutPin);
		BREAK_IF_FAILED(hr, _T("DropFilter pAudioRender"));

		hr = E_FAIL;
		pVideoInPin = InputVideoPinOf(pFileWriter);
		BREAK_IF_NULL(pVideoInPin, _T("pVideoInPin InputVideoPinOf"));

		hr = E_FAIL;
		pAudioInPin = InputAudioPinOf(pFileWriter);
		BREAK_IF_NULL(pAudioInPin, _T("pAudioInPin InputAudioPinOf"));

		hr = ConnectPins(mGraph, pVideoOutPin, pVideoInPin);
		//BREAK_IF_FAILED(hr, _T("ConnectPins(mGraph, pVideoOutPin, pVideoInPin)"));

		hr = ConnectPins(mGraph, pAudioOutPin, pAudioInPin);
		//BREAK_IF_FAILED(hr, _T("ConnectPins(mGraph, pAudioOutPin, pAudioInPin)"));

	} while (false);
	SAFE_RELEASE(pAudioInPin);
	SAFE_RELEASE(pVideoInPin);
	SAFE_RELEASE(pAudioOutPin);
	SAFE_RELEASE(pVideoOutPin);
	return hr;
}

HRESULT CDXGraph::TryInsertTitleOverlayFilter(IBaseFilter* pTilteOverlayFilter, 
											  IPin* pPinOut, IPin* pPinIn, AM_MEDIA_TYPE* pMediaType)
{
	if (pTilteOverlayFilter == NULL || pPinOut == NULL || 
		pPinIn == NULL || pMediaType == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;
	IPin* pPinInOverlay = NULL;
	IPin* pPinOutOverlay = NULL;
	do 
	{
		pPinInOverlay = InputPinOf(pTilteOverlayFilter);
		hr = mGraph->ConnectDirect(pPinOut, pPinInOverlay, pMediaType);
		BREAK_IF_FAILED(hr, _T("ConnectDirect pPinOut pPinInOverlay"));
		pPinOutOverlay = OutputPinOf(pTilteOverlayFilter);
		hr = mGraph->ConnectDirect(pPinOutOverlay, pPinIn, pMediaType);
		BREAK_IF_FAILED(hr, _T("ConnectDirect pPinOutOverlay pPinIn"));
	} while (false);

	if (FAILED(hr))
	{
		pPinOut->Disconnect();
		if(pPinInOverlay != NULL)
			pPinInOverlay->Disconnect();
		pPinIn->Disconnect();
		if(pPinOutOverlay != NULL)
			pPinOutOverlay->Disconnect();
		g_Log.Info(_T("TryInsertTitleOverlayFilter Failed:"));
		PrintMediaType(pMediaType);
	}
	SAFE_RELEASE(pPinInOverlay);
	SAFE_RELEASE(pPinOutOverlay);
	return hr;
}

// Locate a filter within the graph by searching (from renderers upstream)
// looking for a specific interface on the filter
HRESULT CDXGraph::FindFilterByInterface(REFIID riid, IBaseFilter** ppFilter)
{
	*ppFilter = NULL;

	IEnumFilters* pEnum;
	HRESULT hr = mGraph->EnumFilters(&pEnum);
	if (FAILED(hr)) 
	{
		return hr;
	}

	IBaseFilter* pFilter = NULL;
	while (pEnum->Next(1, &pFilter, NULL) == S_OK) 
	{
		// Check for required interface
		IUnknown* pUnk;
		HRESULT hrQuery = pFilter->QueryInterface(riid, (void**)&pUnk);
		if (SUCCEEDED(hrQuery)) 
		{
			pUnk->Release();
			pEnum->Release();
			*ppFilter = pFilter;
			return S_OK;
		}
		pFilter->Release();
	}
	pEnum->Release();

	return E_FAIL;
}

// Find the first pin of a specific direction on a given filter
IPin* CDXGraph::GetPin(IBaseFilter* pFilter, PIN_DIRECTION dirRequest)
{
	IPin * foundPin = NULL;

	IEnumPins* pEnum = NULL;
	HRESULT hr = pFilter->EnumPins(&pEnum);
	if (SUCCEEDED(hr)) 
	{
		IPin* pPin = NULL;
		while (!foundPin && pEnum->Next(1, &pPin, 0) == S_OK) 
		{
			PIN_DIRECTION dir;
			pPin->QueryDirection(&dir);
			if (dir == dirRequest) 
			{
				foundPin = pPin;
			}
			else
			{
				pPin->Release();
			}
		}
		pEnum->Release();
	}
	return foundPin;
}

bool CheckPinType(IPin* pPin, GUID mediaType)
{
	IEnumMediaTypes* pEnumMediaTypes = NULL;
	pPin->EnumMediaTypes(&pEnumMediaTypes);
	CMediaType* pMediaType = NULL;
	ULONG ulMediaCount = 0;

	bool res = false;
	for (;;)
	{
		HRESULT hr = pEnumMediaTypes->Next(1, (AM_MEDIA_TYPE**)&pMediaType, &ulMediaCount);
		if(hr != S_OK)
			break;
		if (pMediaType->majortype == mediaType)
			res = true;

		DeleteMediaType(pMediaType);
		pMediaType = NULL;
		if(res)
			break;
	}
	pEnumMediaTypes->Release();

	return res;
}

// Find the first pin of a specific direction on a given filter
IPin* CDXGraph::GetPin(IBaseFilter* pFilter, PIN_DIRECTION dirRequest, GUID mediaType)
{
	IPin * foundPin = NULL;

	IEnumPins* pEnum = NULL;
	HRESULT hr = pFilter->EnumPins(&pEnum);
	if (SUCCEEDED(hr)) 
	{
		IPin* pPin = NULL;
		while (!foundPin && pEnum->Next(1, &pPin, 0) == S_OK) 
		{
			PIN_DIRECTION dir;
			pPin->QueryDirection(&dir);
			if (dir == dirRequest && CheckPinType(pPin, mediaType)) 
			{
				foundPin = pPin;
			}
			else
			{
				pPin->Release();
			}
		}
		pEnum->Release();
	}
	return foundPin;
}
